using System;
using Microsoft.SPOT;

namespace LightOMator.Color
{
    public class FaderController : IColorController
    {
        public enum FadeMode
        {
            DirectFade, DarkFade, NoFade
        }

        private enum FadePhase
        {
            FadeIn, Keep, FadeOut
        }

        public class Fade
        {
            public RgbColor Color { get; set; }
            public long FadeInTimeMs { get; set; }
            public long FadeOutTimeMs { get; set; }
            public long KeedOnTimeMs { get; set; }
        }


        public Fade[] Fades { get; set; }
        public FadeMode Mode { get; set; }

        private int _currentFadeIndex = -1;
        private FadePhase _currentFadePhase;
        private long _currentFadePhaseStartTicks;

        public RgbColor FirstColor(long ticks)
        {
            _currentFadeIndex = 0;
            _currentFadePhase = FadePhase.Keep;
            _currentFadePhaseStartTicks = ticks;
            return Fades[0].Color;
        }

        public RgbColor NextColor(long ticks)
        {
            Fade fade = Fades[_currentFadeIndex];
            long msSinceStart = (ticks - _currentFadePhaseStartTicks) / 10000L;
            bool switchToNextFade = false;
            if (_currentFadePhase == FadePhase.Keep)
            {
                if (msSinceStart <= fade.KeedOnTimeMs)
                {
                    return null;
                }
                if (Mode == FadeMode.DarkFade)
                {
                    _currentFadePhase = FadePhase.FadeOut;
                    msSinceStart = 0;
                    _currentFadePhaseStartTicks = ticks;
                }
                else
                {
                    switchToNextFade = true;
                }
            }
            Fade previousFade = null;
            if (_currentFadePhase == FadePhase.FadeOut)
            {
                if (msSinceStart <= fade.FadeOutTimeMs)
                {
                    return FadeColor(fade.Color, RgbColor.ColorBlack, msSinceStart, fade.FadeOutTimeMs);
                }
                switchToNextFade = true;
            }
            if (!switchToNextFade)
            {
                if (msSinceStart >= fade.FadeInTimeMs)
                {
                    if (fade.KeedOnTimeMs == 0)
                    {
                        switchToNextFade = true;
                    }
                    else
                    {
                        _currentFadePhase = FadePhase.Keep;
                        msSinceStart = 0;
                        return null;
                    }
                }
            }
            if (switchToNextFade)
            {
                msSinceStart = 0;
                _currentFadePhase = FadePhase.FadeIn;
                _currentFadePhaseStartTicks = ticks;
                int nextFadeIndex = _currentFadeIndex + 1;
                if (nextFadeIndex >= Fades.Length)
                {
                    nextFadeIndex = 0;
                }
                _currentFadeIndex = nextFadeIndex;
                previousFade = fade;
                fade = Fades[nextFadeIndex];
            }
            else
            {
                int previousFadeIndex = _currentFadeIndex - 1;
                if (previousFadeIndex < 0)
                {
                    previousFadeIndex = Fades.Length - 1;
                }
                previousFade = Fades[previousFadeIndex];
            }
            return FadeColor(previousFade.Color, fade.Color, msSinceStart, fade.FadeInTimeMs);
        }

        private RgbColor FadeColor(RgbColor fromColor, RgbColor toColor, long elapsedTime, long totalDuration)
        {
            if (elapsedTime >= totalDuration)
            {
                return toColor;
            }
            if (elapsedTime == 0)
            {
                return fromColor;
            }
            double factor = ((double)elapsedTime) / ((double)totalDuration);
            if (elapsedTime > 300)
            {
                factor = ((double)elapsedTime) / ((double)totalDuration);
            }
            RgbColor result = new RgbColor
            (
                FadeColor(fromColor.Red, toColor.Red, factor),
                FadeColor(fromColor.Green, toColor.Green, factor),
                FadeColor(fromColor.Blue, toColor.Blue, factor)
            );
            return result;
        }

        private byte FadeColor(byte from, byte to, double factor)
        {
            int diff = (int)((from - to) * factor);
            byte result = (byte)(from - diff);
            return result;
        }

    }
}
